//编译器
//   用法   new Compile(el, vm)    el 要遍历和分析的元素  vm指定现在vue的实例
class Compile {
    constructor(el, vm) {
        //el的可能是一个选择器，也可能是一个元素，需要分析 ---》这里不做探究
        //要遍历的宿主节点
        this.$el = document.querySelector(el);
        this.$vm = vm
        //判断el的存在，存在的话需要进行编译
        if(this.$el) {
            //转换内部的内容为片段
            this.$fragment = this.nodeToFragment(this.$el)
            //执行编译
            this.compile(this.$fragment)
            //将编译完的结果html追加到$el
            this.$el.appendChild(this.$fragment)
        }
    }
    //将宿主元素中的代码片段拿出来遍历 ----》 高效
    nodeToFragment(el) {
        const frag = document.createDocumentFragment()
        //将el中的所有的子元素搬家到frag中
        let child;
        while(child = el.firstChild) {
            frag.appendChild(child)  //移动节点
        }
        return frag
    }
    //编译过程   ---节点的类型 --》dom节点还是文本节点
    compile(el) {
        const childNode = el.childNodes;
        Array.from(childNode).forEach(item => {
            //节点的类型判断
            if(this.isElement(item)){
               //元素
             //  console.log('编译元素',item.nodeName)
                const nodeAttrs = item.attributes;
                //查找属性是k-，@，：
                Array.from(nodeAttrs).forEach(attr => { 
                    const attrName = attr.name  //属性名
                    const exp = attr.value       //属性值
                    if(this.isDirective(attrName)) {
                         //k-text
                         const dir = attrName.substring(2)
                         //普通指令   执行指令   text或者modle
                         this[dir] && this[dir](item, this.$vm, exp)
                    }
                    if(this.isEvent(attrName)) {
                        const dir = attrName.substring(1)
                        this.eventHandler(item, this.$vm, exp, dir)
                    }
                })
            } else if(this.isInterpolation(item)) {
               //文本
               //console.log('编译文本',item.textContent)
               //编译文本
               this.compileText(item)
            }
            //递归子节点
            if(item.childNodes && item.childNodes.length > 0) {
                 this.compile(item)
            }
        })
    }
    isElement(node) {   //元素节点
        return node.nodeType === 1
    }
    //插值文本
    isInterpolation(node) {  //即是文本又是插值语
        return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent);
    }
    //指令
    isDirective(attr) {
       return attr.indexOf('k-') == 0
    }
    //事件
    isEvent(attr) {
        return attr.indexOf('@') == 0
    }
    //  k-etxt
    text(node, vm, exp) {
        this.update(node, vm, exp, 'test')
    }
    // @的处理  item, this.$vm, exp, dir
    eventHandler(node, vm, exp, dir) {
        let fn = vm.$options.methods && vm.$options.methods[exp]
        if(dir && fn) {
            node.addEventListener(dir, fn.bind(vm))
        }
    }
    //双向绑定
    model(node, vm, exp) {
        this.update(node, vm, exp, 'model')
        node.addEventListener('input', e => {
            vm[exp] = e.target.value
        })
    }
    //双向绑定的更新
    modelUpdater(node, value) {
        //input的value
        node.value = value
    }
    //html
    html(node, vm, exp) {
        this.update(node, vm, exp, 'html')
        node.addEventListener('input', e => {
            vm[exp] = e.target.value
        })
    }
    //双向绑定的更新
    htmlUpdater(node, value) {
        //input的value
        node.innerHTML = value
    }
     //编译文本
    compileText(node) {
        // console.log(RegExp.$1)
        // node.textContent = this.$vm.$data[RegExp.$1]
        this.update(node, this.$vm, RegExp.$1, 'test')
    }
    //更新函数
    update(node, vm, exp, dir) { 
        //console.log(exp,999)
        //node -->节点    vm --> vue的实例   exp --> 表达式   dir --> 指令(更新的是文本还是指令)
        const updaterFun = this[dir+'Updater']
        //初始化
        updaterFun && updaterFun(node,vm[exp])
        //依赖收集
        new Watcher(vm, exp, (value) => {
            updaterFun && updaterFun(node,value)
        })
        
    }
    testUpdater(node, value) {
        node.textContent = value
    }
}